﻿using System;
using System.Collections.Generic;
using System.ServiceModel.Dispatcher;
using log4net;

namespace AirlineServer
{
    public class QueryCacheOperationInvoker : IOperationInvoker
    {
        private readonly IOperationInvoker baseInvoker;

        private readonly ILog log = LogManager.GetLogger(typeof(QueryCacheOperationInvoker));

        public QueryCacheOperationInvoker(IOperationInvoker baseInvoker)
        {
            this.baseInvoker = baseInvoker;
        }

        public object[] AllocateInputs()
        {
            return baseInvoker.AllocateInputs();
        }

        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            var ss = instance as SearchService;
            if (ss != null && inputs.Length == 4 && inputs[0] is string && inputs[1] is string && inputs[2] is DateTime && inputs[3] is IList<string>)
            {
                var cm = ss.ClusterManager;

                var query = string.Format("{0}_{1}_{2}_{3}", inputs[0], inputs[1],
                    ((DateTime) inputs[2]).ToShortDateString(), string.Join(",", (IList<string>) inputs[3]));

                var result = cm.Cache.GetFromCache(query);

                if (result == null)
                {
                    result = baseInvoker.Invoke(instance, inputs, out outputs) as List<AirTravel>;
                    if (result != null)
                    {
                        if (log.IsDebugEnabled)
                        {
                            log.Debug("Adding cache for query " + query);
                        }

                        cm.Cache.AddToCache(query, result);
                    }
                }
                else
                {
                    if (log.IsDebugEnabled)
                    {
                        log.Debug("Retrieved item from cache for query " + query);
                    }

                    outputs = new object[0];
                }

                return result;
            }

            return baseInvoker.Invoke(instance, inputs, out outputs);
        }

        public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
        {
            return baseInvoker.InvokeBegin(instance, inputs, callback, state);
        }

        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
        {
            return baseInvoker.InvokeEnd(instance, out outputs, result);
        }

        public bool IsSynchronous
        {
            get { return baseInvoker.IsSynchronous; }
        }
    }
}
